#ifndef _SIUT_SIMD_STANDARD_FRUSTUM_HPP_
#define _SIUT_SIMD_STANDARD_FRUSTUM_HPP_

#include "Vec3f.hpp"
#include "Vec4f.hpp"
#include "Mat4f.hpp"
#include "HPlane3f.hpp"

namespace siut
{
namespace simd
{

  struct Frustum
  {
    HPlane3f bottom_;
    HPlane3f far_;
    HPlane3f left_;
    HPlane3f near_;
    HPlane3f top_;
    HPlane3f right_;

    inline Frustum()
    {

    }
#ifndef NVCC
    inline Frustum(HPlane3f &left, HPlane3f &right, HPlane3f &bottom, HPlane3f &top,
	    HPlane3f &near, HPlane3f &far)
      :bottom_(bottom),
       far_(far),
       left_(left),
       near_(near),
       top_(top),
       right_(right)
    {

    }

    inline Frustum(const Vec3f &bottomRightNear, const Vec3f &bottomLeftNear,
	    const Vec3f &bottomRightFar, const Vec3f &bottomLeftFar,
	    const Vec3f &topRightNear, const Vec3f &topLeftNear,
	    const Vec3f &topRightFar, const Vec3f &topLeftFar)
      :bottom_(bottomLeftFar, bottomLeftNear, bottomRightFar),
       far_(bottomLeftFar, bottomRightFar, topRightFar),
       left_(topLeftNear, bottomLeftNear, bottomLeftFar),
       near_(bottomRightNear, bottomLeftNear, topRightNear),
       top_(topLeftNear, topRightFar, topRightNear),
       right_(bottomRightNear, topRightNear, bottomRightFar)
    {
      (void)topLeftFar;
    }

    inline Frustum(const Mat4f &m)
    {
      bottom_ = HPlane3f(get(m, 3,0) + get(m, 1,0),
			 get(m, 3,1) + get(m, 1,1),
			 get(m, 3,2) + get(m, 1,2),
			 get(m, 3,3) + get(m, 1,3));
      
      far_ = HPlane3f(get(m, 3,0) + get(m, 2,0),
		      get(m, 3,1) + get(m, 2,1),
		      get(m, 3,2) + get(m, 2,2),
		      get(m, 3,3) + get(m, 2,3));

      left_ = HPlane3f(get(m, 3,0) + get(m, 0,0),
		       get(m, 3,1) + get(m, 0,1),
		       get(m, 3,2) + get(m, 0,2),
		       get(m, 3,3) + get(m, 0,3));

      near_ = HPlane3f(get(m, 3,0) - get(m, 2,0),
		      get(m, 3,1) - get(m, 2,1),
		      get(m, 3,2) - get(m, 2,2),
		      get(m, 3,3) - get(m, 2,3));
      
      right_ = HPlane3f(get(m, 3,0) - get(m, 0,0),
			get(m, 3,1) - get(m, 0,1),
			get(m, 3,2) - get(m, 0,2),
			get(m, 3,3) - get(m, 0,3));

     
      top_ = HPlane3f(get(m, 3,0) - get(m, 1,0),
		      get(m, 3,1) - get(m, 1,1),
		      get(m, 3,2) - get(m, 1,2),
		      get(m, 3,3) - get(m, 1,3));

    }
  };
#endif

    inline bool insideFast(const Frustum &f, const Vec3f &p)
    {
      return insideFast((HPlane3f&)f.bottom_, (Vec3f&)p) &&
	insideFast((HPlane3f&)f.far_, (Vec3f&)p) &&
	insideFast((HPlane3f&)f.left_, (Vec3f&)p) &&
	insideFast((HPlane3f&)f.near_, (Vec3f&)p) &&
	insideFast((HPlane3f&)f.right_, (Vec3f&)p) &&
	insideFast((HPlane3f&)f.top_, (Vec3f&)p);
    }

    inline void normalize(Frustum &f)
    {
      normalize(f.far_);
      normalize(f.left_);
      normalize(f.near_);
      normalize(f.right_);
      normalize(f.top_);
    }

    inline Vec3f getTopLeftNear(const Frustum &f)
    {
      return intersection(f.top_, f.left_, f.near_);
    }

    inline Vec3f getTopRightNear(const Frustum &f) 
    {
      return intersection(f.top_, f.right_, f.near_);
    }

    inline Vec3f getBottomLeftNear(const Frustum &f) 
    {
      return intersection(f.bottom_, f.left_, f.near_);
    }

    inline Vec3f getBottomRightNear(const Frustum &f)
    {
      return intersection(f.bottom_, f.right_, f.near_);
    }

    inline Vec3f getTopLeftFar(const Frustum &f) 
    {
      return intersection(f.top_, f.left_, f.far_);
    }

    inline Vec3f getTopRightFar(const Frustum &f)
    {
      return intersection(f.top_, f.right_, f.far_);
    }

    inline Vec3f getBottomLeftFar(const Frustum &f) 
    {
      return intersection(f.bottom_, f.left_, f.far_);
    }

    inline Vec3f getBottomRightFar(const Frustum &f) 
    {
      return intersection(f.bottom_, f.right_, f.far_);
    }

    inline void setFar(Frustum &f, const HPlane3f &p)
    {
      f.far_ = p;
    }

    inline void setNear(Frustum &f, const HPlane3f &p)
    {
      f.near_ = p;
    }

    inline void setRight(Frustum &f, const HPlane3f &p)
    {
      f.right_ = p;
    }

    inline void setLeft(Frustum &f, const HPlane3f &p)
    {
      f.left_ = p;
    }

    inline void setTop(Frustum &f, const HPlane3f &p)
    {
      f.top_ = p;
    }

    inline void setBottom(Frustum &f, const HPlane3f &p)
    {
      f.bottom_ = p;
    }



}//end namespaces
}

#endif
